package com.example.onlineanswer.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.onlineanswer.mapper.CertificateMapper;
import com.example.onlineanswer.mapper.ContestMapper;
import com.example.onlineanswer.mapper.ParticipationMapper;
import com.example.onlineanswer.mapper.SysUserMapper;
import com.example.onlineanswer.model.entity.Certificate;
import com.example.onlineanswer.model.entity.Contest;
import com.example.onlineanswer.model.entity.Participation;
import com.example.onlineanswer.model.entity.SysUser;
import com.example.onlineanswer.model.vo.CertificateVO;
import com.example.onlineanswer.service.CertificateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.core.io.Resource;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Slf4j
@Service
public class CertificateServiceImpl implements CertificateService {

    private final CertificateMapper certificateMapper;
    private final ParticipationMapper participationMapper;
    private final ContestMapper contestMapper;
    private final SysUserMapper sysUserMapper;

    @Value("${app.certificate.storage-path:data/certificates}")
    private String certificateStoragePath;

    private static final String CERTIFICATE_BG_PATH = "/static/images/certificate-bg.png";
    private static final String STAMP_PATH = "/static/images/stamp.png";

    public CertificateServiceImpl(CertificateMapper certificateMapper,
                                  ParticipationMapper participationMapper,
                                  ContestMapper contestMapper,
                                  SysUserMapper sysUserMapper) {
        this.certificateMapper = certificateMapper;
        this.participationMapper = participationMapper;
        this.contestMapper = contestMapper;
        this.sysUserMapper = sysUserMapper;
    }

    @PostConstruct
    public void init() {
        // Ensure storage path exists
        try {
            if (certificateStoragePath == null || certificateStoragePath.trim().isEmpty()) {
                log.error("Certificate storage path is not configured or is empty. Please check 'app.certificate.storage-path' property.");
                // Optionally, throw an exception here if this is a critical failure
                // For now, we'll try to use the default from @Value if it was set by then, but it's risky.
                // A better approach is to ensure it's set or throw an IllegalStateException.
                if (this.certificateStoragePath == null) { // Check again, @Value might not have run if this PostConstruct is too early for some reason
                    this.certificateStoragePath = "data/certificates"; // Fallback, but not ideal
                    log.warn("Falling back to default certificate storage path: {}", this.certificateStoragePath);
                }
            }
            Files.createDirectories(Paths.get(this.certificateStoragePath));
            log.info("Certificate storage directory ensured at: {}", this.certificateStoragePath);
        } catch (IOException e) {
            log.error("Could not create certificate storage directory: {}", this.certificateStoragePath, e);
            // Consider throwing a runtime exception if storage is critical
        }
    }

    @Override
    @Transactional
    public CertificateVO getUserCertificateForContest(Long userId, Long contestId) {
        // 1. Find participation for the user in the specific contest
        Participation participation = participationMapper.selectOne(
                new LambdaQueryWrapper<Participation>()
                        .eq(Participation::getUserId, userId)
                        .eq(Participation::getContestId, contestId)
        );

        if (participation == null) {
            log.info("No participation record found for user {} in contest {}", userId, contestId);
            return null; // No participation, so no certificate
        }

        Contest contest = contestMapper.selectById(participation.getContestId()); // or contestId param directly
        if (contest == null || !contest.getCertificateEnabled() || participation.getRank() == null || participation.getRank() == 0) {
            log.info("Contest {} not found, or certificate not enabled, or user {} has no rank for this contest.", contestId, userId);
            return null; // No certificate for this contest or no rank or certs disabled
        }

        // 2. Check if user is eligible for a certificate based on rank and contest settings
        if (participation.getRank() > 0 && participation.getRank() <= contest.getCertificateCount()) {
            // 3. Check if certificate already exists
            Certificate certificate = certificateMapper.selectOne(
                    new LambdaQueryWrapper<Certificate>().eq(Certificate::getParticipationId, participation.getId())
            );

            if (certificate == null) {
                // 4. Generate certificate if eligible and not existing
                SysUser user = sysUserMapper.selectById(userId);
                if (user != null) {
                    log.info("Generating certificate for user {} in contest {}", userId, contestId);
                    certificate = generateAndSaveCertificate(participation, contest, user);
                } else {
                    log.warn("User with ID {} not found, cannot generate certificate.", userId);
                    return null;
                }
            }

            if (certificate != null) {
                return convertToVO(certificate, contest);
            }
        }
        log.info("User {} in contest {} is not eligible for a certificate based on rank {} and contest certificate count {}.", 
                 userId, contestId, participation.getRank(), contest.getCertificateCount());
        return null; // Not eligible
    }

    private Certificate generateAndSaveCertificate(Participation participation, Contest contest, SysUser user) {
        String certificateNumber = "CERT-" + UUID.randomUUID().toString().substring(0, 12).toUpperCase();
        String fileName = certificateNumber + ".png";
        Path filePath = Paths.get(certificateStoragePath, fileName);

        try {
            // Load background and stamp images
            InputStream bgStream = getClass().getResourceAsStream(CERTIFICATE_BG_PATH);
            InputStream stampStream = getClass().getResourceAsStream(STAMP_PATH);

            if (bgStream == null) {
                log.error("Certificate background image not found: {}", CERTIFICATE_BG_PATH);
                // Attempt to load from file system as a fallback for development/testing
                // Path bgResourcePath = Paths.get("src/main/resources" + CERTIFICATE_BG_PATH);
                // if(Files.exists(bgResourcePath)) bgStream = Files.newInputStream(bgResourcePath);
                // else return null;
                return null; 
            }
            // Stamp is optional, proceed if not found
            // if (stampStream == null) {
            //     log.warn("Certificate stamp image not found: {}. Proceeding without stamp.", STAMP_PATH);
            // }

            BufferedImage bgImage = ImageIO.read(bgStream);
            BufferedImage certificateImage = new BufferedImage(bgImage.getWidth(), bgImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = certificateImage.createGraphics();

            // Draw background
            g2d.drawImage(bgImage, 0, 0, null);

            // Set text properties
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2d.setColor(Color.decode("#4A4A4A")); // Dark grey color for text

            // User Name (Nickname or Username)
            String displayName = user.getNickname() != null && !user.getNickname().isEmpty() ? user.getNickname() : user.getUsername();
            drawText(g2d, displayName + " 同学:", new Font("微软雅黑", Font.BOLD, 48), 150, 350, bgImage.getWidth() - 300);

            // Contest Name
            drawText(g2d, "在 \"" + contest.getTitle() + "\" 竞赛中", new Font("微软雅黑", Font.PLAIN, 36), 150, 430, bgImage.getWidth() - 300);

            // Rank
            drawText(g2d, "荣获 " + getRankString(participation.getRank()) + "!", new Font("微软雅黑", Font.BOLD, 42), 150, 510, bgImage.getWidth() - 300);

            // Issue Date
            String issueDateStr = "颁发日期: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
            drawText(g2d, issueDateStr, new Font("微软雅黑", Font.PLAIN, 28), 150, 650, bgImage.getWidth() - 300);

            // Certificate Number
            drawText(g2d, "证书编号: " + certificateNumber, new Font("微软雅黑", Font.PLAIN, 24), bgImage.getWidth() - 450, bgImage.getHeight() - 80, 400);

            // Draw stamp (if exists)
            if (stampStream != null) {
                BufferedImage stampImage = ImageIO.read(stampStream);
                // Scale to 1/4 of original size
                int stampWidth = stampImage.getWidth() / 8;
                int stampHeight = stampImage.getHeight() / 8;
                
                // Position more towards the bottom-right corner, e.g., 50px offset
                int marginRight = 200; // Adjust as needed
                int marginBottom = 175; // Adjust as needed
                int stampX = certificateImage.getWidth() - stampWidth - marginRight;
                int stampY = certificateImage.getHeight() - stampHeight - marginBottom;
                
                g2d.drawImage(stampImage, stampX, stampY, stampWidth, stampHeight, null); 
                stampStream.close(); 
            }
            bgStream.close(); // Close the stream

            g2d.dispose();

            // Save the image
            ImageIO.write(certificateImage, "PNG", filePath.toFile());
            log.info("Certificate generated: {}", filePath.toString());

            // Save certificate record to database
            Certificate certEntity = new Certificate();
            certEntity.setParticipationId(participation.getId());
            certEntity.setCertificateNo(certificateNumber);
            // certEntity.setTemplateId(); // If you have templates
            certEntity.setCertificateUrl(fileName); // Store relative path or just filename
            certEntity.setIssueTime(LocalDateTime.now());
            certEntity.setCreateTime(LocalDateTime.now());
            certEntity.setUpdateTime(LocalDateTime.now());
            certificateMapper.insert(certEntity);
            return certEntity;

        } catch (IOException e) {
            log.error("Error generating certificate image for participation ID {}: {}", participation.getId(), e.getMessage(), e);
            return null;
        }
    }
    
    // Overloaded drawText to handle potential line breaks or width constraints if needed in future
    private void drawText(Graphics2D g2d, String text, Font font, int x, int y, int maxWidth) {
        g2d.setFont(font);
        FontMetrics fm = g2d.getFontMetrics();
        // Basic implementation, does not handle line wrapping.
        // For line wrapping, you would need to break the string into multiple lines.
        g2d.drawString(text, x, y + fm.getAscent());
    }

    private String getRankString(Integer rank) {
        if (rank == null || rank == 0) return "优胜奖"; // Default for null or 0 rank
        switch (rank) {
            case 1: return "一等奖";
            case 2: return "二等奖";
            case 3: return "三等奖";
            default:
                if (rank <= 10) return "优胜奖"; // Example for top 10
                return "参与证书"; // Or some other generic term
        }
    }

    private CertificateVO convertToVO(Certificate certificate, Contest contest) {
        CertificateVO vo = new CertificateVO();
        
        // Manual mapping to ensure correct field assignment
        vo.setId(certificate.getId());
        vo.setCertificateNumber(certificate.getCertificateNo()); // Map certificateNo to certificateNumber
        vo.setIssueDate(certificate.getIssueTime());         // Map issueTime to issueDate
        vo.setContestId(contest.getId());
        vo.setContestName(contest.getTitle());

        Participation participation = participationMapper.selectById(certificate.getParticipationId());
        if(participation != null) {
            SysUser user = sysUserMapper.selectById(participation.getUserId());
            if(user != null) {
                 vo.setUserName(user.getNickname() != null && !user.getNickname().isEmpty() ? user.getNickname() : user.getUsername());
            }
            vo.setRankString(getRankString(participation.getRank()));
        }
        // The downloadUrl should be relative to the API base, or a full URL if constructed differently
        vo.setDownloadUrl("/api/certificates/download/" + certificate.getId());
        return vo;
    }

    @Override
    public Resource loadCertificateFile(Long certificateId, Long userId) {
        Certificate certificate = certificateMapper.selectById(certificateId);
        if (certificate == null) {
            log.warn("Certificate not found with ID: {}", certificateId);
            return null;
        }

        // Authorization check: ensure the user requesting is the owner of the certificate
        // or an admin (admin check not implemented here for simplicity)
        Participation participation = participationMapper.selectById(certificate.getParticipationId());
        if (participation == null || !participation.getUserId().equals(userId)) {
            // TODO: Add role check for admin if admins are allowed to download any certificate
            log.warn("User {} (or non-owner) attempted to download certificate {} belonging to user {}", userId, certificateId, participation != null ? participation.getUserId() : "unknown");
            return null; // Or throw an AccessDeniedException
        }

        try {
            Path filePath = Paths.get(this.certificateStoragePath).resolve(certificate.getCertificateUrl()).normalize();
            Resource resource = new UrlResource(filePath.toUri());
            if (resource.exists() && resource.isReadable()) {
                return resource;
            }
            log.error("Certificate file not found or not readable: {}", filePath.toString());
            return null;
        } catch (MalformedURLException e) {
            log.error("Error creating URL for certificate file {}: {}", certificate.getCertificateUrl(), e.getMessage(), e);
            return null;
        }
    }

    @Override
    public List<CertificateVO> getUserCertificates(Long userId) {
        List<CertificateVO> result = new ArrayList<>();
        
        // 获取用户的所有参与记录
        LambdaQueryWrapper<Participation> partWrapper = new LambdaQueryWrapper<>();
        partWrapper.eq(Participation::getUserId, userId);
        List<Participation> participations = participationMapper.selectList(partWrapper);
        
        // 如果没有参与记录，直接返回空列表
        if (participations == null || participations.isEmpty()) {
            return result;
        }
        
        // 对每个参与记录查找关联的证书
        for (Participation participation : participations) {
            LambdaQueryWrapper<Certificate> certWrapper = new LambdaQueryWrapper<>();
            certWrapper.eq(Certificate::getParticipationId, participation.getId());
            Certificate certificate = certificateMapper.selectOne(certWrapper);
            
            // 如果存在证书，构建VO并添加到结果列表
            if (certificate != null) {
                // 获取竞赛信息
                Contest contest = contestMapper.selectById(participation.getContestId());
                if (contest == null) continue; // 如果竞赛不存在，跳过
                
                CertificateVO vo = new CertificateVO();
                vo.setId(certificate.getId());
                vo.setContestId(participation.getContestId());
                vo.setContestName(contest.getTitle());
                vo.setCertificateNumber(certificate.getCertificateNo());
                vo.setIssueDate(certificate.getIssueTime());
                
                // 设置排名描述
                if (participation.getRank() != null) {
                    switch (participation.getRank()) {
                        case 1:
                            vo.setRankString("第一名");
                            break;
                        case 2:
                            vo.setRankString("第二名");
                            break;
                        case 3:
                            vo.setRankString("第三名");
                            break;
                        default:
                            vo.setRankString("第" + participation.getRank() + "名");
                    }
                } else {
                    vo.setRankString("参与奖");
                }
                
                // 设置下载URL
                vo.setDownloadUrl("/api/certificates/download/" + certificate.getId());
                
                result.add(vo);
            }
        }
        
        return result;
    }
} 